package al.work.httplisten;

import al.work.common.define.Pair;
import al.work.common.util.ClassUtils;
import al.work.httplisten.aop.annotation.ControllerException;
import al.work.httplisten.aop.annotation.ExceptionHandler;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author JiuZh
 */
public class HttpExceptionHandler {

    private final Map<Class<? extends Throwable>, List<Pair<Object, Method>>> handler = new HashMap<>();

    public void initialize(String name) throws Exception {
        Map<Class<?>, ControllerException> classes = ClassUtils.getAnnotationClasses(name, ControllerException.class);
        for (Map.Entry<Class<?>, ControllerException> entry : classes.entrySet()) {
            Class<?> classis = entry.getKey();
            Object handlerT = classis.newInstance();

            Map<Method, ExceptionHandler> methods = ClassUtils.getAnnotationMethod(classis, ExceptionHandler.class);
            for (Map.Entry<Method, ExceptionHandler> method : methods.entrySet()) {
                Class<? extends Throwable>[] throwables = method.getValue().value();
                for (Class<? extends Throwable> throwable : throwables) {
                    List<Pair<Object, Method>> list = handler.get(throwable);
                    if (null == list) {
                        list = new ArrayList<>();
                        handler.put(throwable, list);
                    }
                    list.add(new Pair<>(handlerT, method.getKey()));
                }
            }
        }
    }

    public <T extends Throwable> Object handler (T throwable) {
        List<Pair<Object, Method>> list = null;
        Class<?> classis = throwable.getClass();
        while(null == list && !classis.equals(Throwable.class)) {
            list = handler.get(classis);
            classis = classis.getSuperclass();
        }
        if (null == list) {
            return null;
        }
        Object result = null;
        for (Pair<Object, Method> kv : list) {
            try {
                Object rt = kv.right.invoke(kv.left, throwable);
                if (null != rt) {
                    result = rt;
                }
            } catch (Exception ex) {}
        }
        return result;
    }
}
